package com.njxxted.activiti.modules.process.task.service.impl;

import com.google.common.collect.Maps;
import com.njxxted.activiti.common.constant.Constant;
import com.njxxted.activiti.common.mode.HandlerContext;
import com.njxxted.activiti.common.response.ResponseVo;
import com.njxxted.activiti.common.util.BpmnBuilderUtil;
import com.njxxted.activiti.common.util.StringUtils;
import com.njxxted.activiti.common.util.WorkflowUtils;
import com.njxxted.activiti.modules.base.service.BaseService;
import com.njxxted.activiti.modules.process.cmd.TaskHandleCmd;
import com.njxxted.activiti.modules.process.defintion.dao.ActDefinitionDao;
import com.njxxted.activiti.modules.process.defintion.dao.ActRunDefinitionDao;
import com.njxxted.activiti.modules.process.defintion.entity.ActNodeConfigEntity;
import com.njxxted.activiti.modules.process.defintion.entity.ActRunNodeConfigEntity;
import com.njxxted.activiti.modules.process.task.handler.AbstractTaskStrategyHandler;
import com.njxxted.activiti.modules.process.task.service.IActTaskService;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.persistence.entity.UserEntityImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * 描述：流程任务 service
 * <p>
 * 作者：Ostrich Hu
 * 时间：2019/9/5 16:41 星期四
 */
@Service
public class IActTaskServiceImpl extends BaseService implements IActTaskService {

    @Resource
    private IdentityService identityService;
    @Resource
    private HistoryService historyService;
    @Resource
    private ActDefinitionDao actDefinitionDao;
    @Resource
    private ActRunDefinitionDao actRunDefinitionDao;
    @Resource
    private HandlerContext handlerContext;

    @Override
    public Map<String, Object> getNextNodeCandidateUser(HttpServletRequest request, String type) {
        Map<String, Object> resultMap = Maps.newHashMap();
        //构造参数
        TaskHandleCmd taskCmd = BpmnBuilderUtil.buildTaskHandleCmd(request);
        List<FlowNode> nextNode = WorkflowUtils.getNextNode(taskCmd.getProcessDefId(), taskCmd.getCurrNodeKey(), taskCmd.getVariables());
        if (nextNode.size() == 0) {
            return null;
        }
        FlowNode flowNode = nextNode.get(0);
        //候选人集合
        CopyOnWriteArraySet<Map> assignUsers = new CopyOnWriteArraySet<>();
        //抄送人集合
        CopyOnWriteArraySet<Map> copyUsers = new CopyOnWriteArraySet<>();
        //查询出所有人员信息
        List<User> userList = identityService.createUserQuery().list();
        //type如果是催办，查询运行时的配置信息
        if (StringUtils.isNotBlank(type) && "urgent".equals(type)) {
            ActRunNodeConfigEntity actRunNodeConfigEntity = new ActRunNodeConfigEntity();
            actRunNodeConfigEntity.setRunTaskId(taskCmd.getCurrNodeId());
            List<ActRunNodeConfigEntity> definitionRunSettings = actRunDefinitionDao.getProcessDefinitionSettings(actRunNodeConfigEntity);
            List<ActRunNodeConfigEntity> assignConfigs = definitionRunSettings.parallelStream()
                    .filter(config -> config.getConfigType().contains("assign:")).collect(Collectors.toList());
            List<ActRunNodeConfigEntity> copyConfigs = definitionRunSettings.parallelStream()
                    .filter(conf -> conf.getConfigType().contains("copy:")).collect(Collectors.toList());
            //设置
            userList.parallelStream().forEach(user -> {
                //候选人
                assignUsers.addAll(matchedRunUser(user, assignConfigs, "assign" , taskCmd.getInstanceId()));
                //抄送人
                copyUsers.addAll(matchedRunUser(user, copyConfigs, "copy" , taskCmd.getInstanceId()));
            });
        } else {
            ActNodeConfigEntity nodeConfigEntity = new ActNodeConfigEntity();
            nodeConfigEntity.setKey(taskCmd.getKey());
            nodeConfigEntity.setNodeId(flowNode.getId());
            //查询出目标节点的配置信息
            List<ActNodeConfigEntity> definitionSettings = actDefinitionDao.getProcessDefinitionSettings(nodeConfigEntity);
            //过滤出只有办理和抄送人员的配置信息
            List<ActNodeConfigEntity> assignConfigs = definitionSettings.parallelStream()
                    .filter(config -> config.getConfigType().contains("assign:")).collect(Collectors.toList());
            List<ActNodeConfigEntity> copyConfigs = definitionSettings.parallelStream()
                    .filter(conf -> conf.getConfigType().contains("copy:")).collect(Collectors.toList());
            //设置
            userList.parallelStream().forEach(user -> {
                //候选人
                assignUsers.addAll(matchedUser(user, assignConfigs, "assign" , taskCmd.getInstanceId()));
                //抄送人
                copyUsers.addAll(matchedUser(user, copyConfigs, "copy" , taskCmd.getInstanceId()));
            });
        }

        resultMap.put("candidateInfo" , assignUsers);
        resultMap.put("copyInfo" , copyUsers);
        return resultMap;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public ResponseVo completeTask(HttpServletRequest request) {
        TaskHandleCmd taskCmd = BpmnBuilderUtil.buildTaskHandleCmd(request);
        //根据不同的类型执行不同的策略
        AbstractTaskStrategyHandler instance = (AbstractTaskStrategyHandler) handlerContext.getInstance(taskCmd.getOperateType());
        instance.taskHandle(taskCmd);
        //返回审批后当前节点信息
        Map<String, String> map = Maps.newHashMap();
        List<HistoricActivityInstance> hisActivity = historyService.createHistoricActivityInstanceQuery()
                .orderByHistoricActivityInstanceStartTime().desc()
                .processInstanceId(taskCmd.getInstanceId()).list();
        if (hisActivity.size() > 0) {
            map.put("currNodeId" , hisActivity.get(0).getActivityId());
            map.put("currNodeName" , hisActivity.get(0).getActivityName());
            map.put("instanceId" , taskCmd.getInstanceId());
            map.put("msg" , "任务办理成功");
        }
        return ResponseVo.ok(map);

    }

    /**
     * 转换 user为 map
     *
     * @param user
     * @return
     */
    private Map<String, Object> convertUser(User user, String value, String type, String logic, Date date) {
        Map<String, Object> map = Maps.newHashMap();
        List<Group> roleList = identityService.createGroupQuery().groupMember(user.getId()).groupType("role").list();
        List<Group> departList = identityService.createGroupQuery().groupMember(user.getId()).groupType("depart").list();
        String departIds = departList.parallelStream().map(Group::getId).collect(Collectors.joining(","));
        String roleIds = roleList.parallelStream().map(Group::getId).collect(Collectors.joining(","));
        UserEntityImpl user1 = (UserEntityImpl) user;
        map.put("id" , user.getId());
        map.put("head" , user1.getPictureByteArrayRef().getId());
        map.put("roleId" , roleIds);
        map.put("departId" , departIds);
        map.put("name" , user.getLastName());
        map.put("fullId" , value);
        map.put("type" , type);
        map.put("logic" , logic);
        map.put("addDate" , date);
        return map;
    }

    /**
     * 匹配用户
     *
     * @param user       用户
     * @param configs    配置信息
     * @param type       用户类型
     * @param instanceId 流程实例id
     * @return
     */
    private CopyOnWriteArraySet<Map> matchedUser(User user, List<ActNodeConfigEntity> configs, String type, String instanceId) {
        CopyOnWriteArraySet<Map> users = new CopyOnWriteArraySet<>();
        StringBuilder sb = new StringBuilder();
        final Map[] mapUser = new Map[1];
        configs.parallelStream().forEachOrdered(config -> {
            mapUser[0] = convertUser(user, config.getConfigValue(), config.getConfigType(), config.getOtherValue(), config.getAddDate());
            sb.append(builderCondition(mapUser[0], type, instanceId, config.getConfigType(), config.getConfigValue(), config.getOtherValue()));
        });
        String condition = sb.toString().replace("and" , " && ").replace("or" , " || ");
        if (StringUtils.isTrue(condition)) {
            users.add(mapUser[0]);
        }
        return users;
    }

    /**
     * 匹配运行时用户
     *
     * @param user       用户
     * @param configs    配置信息
     * @param userType   用户类型
     * @param instanceId 流程实例id
     * @return
     */
    private CopyOnWriteArraySet<Map> matchedRunUser(User user, List<ActRunNodeConfigEntity> configs,
                                                    String userType, String instanceId) {
        CopyOnWriteArraySet<Map> users = new CopyOnWriteArraySet<>();
        StringBuilder sb = new StringBuilder();
        final Map[] mapUser = new Map[1];
        configs.parallelStream().forEachOrdered(config -> {
            mapUser[0] = convertUser(user, config.getConfigValue(), config.getConfigType(), config.getOtherValue(), config.getAddDate());
            sb.append(builderCondition(mapUser[0], userType, instanceId, config.getConfigType(), config.getConfigValue(), config.getOtherValue()));
        });
        String condition = sb.toString().replace("and" , " && ").replace("or" , " || ");
        if (StringUtils.isTrue(condition)) {
            users.add(mapUser[0]);
        }
        return users;
    }

    /**
     * 构建条件
     *
     * @param user        转换之后的用户
     * @param userType    匹配的类型
     * @param instanceId  流程实例id
     * @param configType  配置类型
     * @param configValue 配置的值
     * @param otherValue  配置的其他值
     * @return StringBuilder
     */
    private StringBuilder builderCondition(Map user, String userType, String instanceId,
                                           String configType, String configValue, String otherValue) {
        //查询出流程发起人以及他所在的部门
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId).singleResult();
        List<Group> departs = identityService.createGroupQuery().groupType("depart").groupMember(instance.getStartUserId()).list();
        String departIds = departs.parallelStream().map(Group::getId).collect(Collectors.joining(","));

        StringBuilder sb = new StringBuilder();
        String[] arr = configValue.split("/");
        if ("assign".equals(userType)) {
            switch (configType) {
                case Constant.Flow.ASSIGN_USER:
                    sb.append(arr[arr.length - 1].equals(user.get("id"))).append(otherValue);
                    break;
                case Constant.Flow.ASSIGN_DEPART:
                    sb.append(String.valueOf(user.get("departId")).contains(arr[arr.length - 1])).append(otherValue);
                    break;
                case Constant.Flow.ASSIGN_ROLE:
                    sb.append(String.valueOf(user.get("roleId")).contains(arr[arr.length - 1])).append(otherValue);
                    break;
                case Constant.Flow.ASSIGN_SPONSOR_DEPARTMENT:
                    sb.append(String.valueOf(user.get("departId")).contains(departIds)).append(otherValue);
                    break;
            }
        } else {
            switch (configType) {
                case Constant.Flow.COPY_USER:
                    sb.append(arr[arr.length - 1].equals(user.get("id"))).append(otherValue);
                    break;
                case Constant.Flow.COPY_DEPART:
                    sb.append(String.valueOf(user.get("departId")).contains(arr[arr.length - 1])).append(otherValue);
                    break;
                case Constant.Flow.COPY_ROLE:
                    sb.append(String.valueOf(user.get("roleId")).contains(arr[arr.length - 1])).append(otherValue);
                case Constant.Flow.COPY_SPONSOR_DEPARTMENT:
                    sb.append(String.valueOf(user.get("departId")).contains(departIds)).append(otherValue);
                    break;
            }
        }
        return sb;
    }
}
